using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using System.Diagnostics.CodeAnalysis;      // added: vSv

namespace SRGenerator.CustomTool
{
  public class Processor
  {
    CodeDomProvider _provider;

    public Processor(CodeDomProvider provider)
    {
      if (provider == null)
        throw new ApplicationException("No code provider available.");

      _provider = provider;
    }

    bool _convertPublicToInternal = false;

    public string GenerateResourceFile(string stringsSource, IProcessorFeedback feedback)
    {
      string primaryFileName = null;

      XmlDocument template = new XmlDocument();
      using (Stream s = typeof(Processor).Assembly.GetManifestResourceStream(typeof(Processor).Namespace + ".Template.xml"))
      {
        template.Load(s);
      }


      XmlDocument doc = null;
      XmlElement prevElement = null;
      string locale = string.Empty;

      string line = string.Empty;
      using (TextReader tr = File.OpenText(stringsSource))
      {
        int lineNo = 0;
        while ( (line = tr.ReadLine()) != null)
        {
          lineNo++;
          if (line.Trim().Length == 0)
          {
            prevElement = null;
            continue;
          }

          if (line[0] == ';')
          {
            prevElement = null;
            continue;
          }

          if (ParseHeader(line, template))
            continue;

          // Only process the [strings] section of the file.
          if (line[0] == '[')
          {
            if (doc != null)
            {
              string fileName = SaveResX(stringsSource, locale, doc, feedback);
              if (primaryFileName == null)
              {
                primaryFileName = fileName;
              }
              doc = null;
            }

            if (line.Trim().ToLower().Substring(0,8) == "[strings")
            {
              locale = line.Trim().Substring(8,line.Trim().Length - 9);
              doc = new XmlDocument();
              doc.AppendChild(doc.ImportNode(template.DocumentElement, true)) ;
            }
            continue;
          }

          // Not writing to a resource document.
          if (doc == null)
            continue;


          // Could use regex here, but this is probably faster.
          int split = line.IndexOf('=');
          if (split == -1)
          {
            feedback.LineError("Invalid resource, missing = sign",line,lineNo);                        
            continue;
          }

          string res = line.Substring(0, split).Trim();
          string text = line.Substring(split+1).Trim();
          string comment = null;

          if (res.Length == 0)
          {
            if (prevElement != null)
            {
              prevElement.InnerText = prevElement.InnerText + "\n" + text;
            }
            else
            {
              feedback.LineError("Invalid resource, missing resource name",line,lineNo);
            }
            continue;                        
          }

          int resArgSplit = res.IndexOf('(');
          if (resArgSplit > -1)
          {
            if (resArgSplit == 0)
            {
              feedback.LineError("Invalid resource, no resource name",line,lineNo);                            
              continue;
            }

            int resArgSplit2 = res.IndexOf(')', resArgSplit);
            if (resArgSplit2 == -1)
            {
              feedback.LineError("Invalid resource, missing end bracket on arguments",line,lineNo);
              continue;
            }
            comment = res.Substring(resArgSplit+1, resArgSplit2 - resArgSplit - 1);
            res = res.Substring(0, resArgSplit);
          }

          XmlElement el = doc.CreateElement("data");
          el.SetAttribute("name", res);

          XmlElement valueEl = doc.CreateElement("value");
          valueEl.AppendChild(doc.CreateTextNode(text));
          el.AppendChild(valueEl);

          if (comment != null)
          {
            XmlElement commentEl = doc.CreateElement("comment");
            commentEl.AppendChild(doc.CreateTextNode(comment));
            el.AppendChild(commentEl);
          }

          // Allow appending new lines
          prevElement = valueEl;

          doc.DocumentElement.AppendChild(el);
        }
      }

      if (doc != null)
      {
        string fileName = SaveResX(stringsSource, locale, doc, feedback);
        if (primaryFileName == null)
        {
          primaryFileName = fileName;
        }
        doc = null;
      }

      if (primaryFileName == null)
      {
        feedback.LineError("No [strings] section has been found.",string.Empty,0);
        return null;
      }

      return primaryFileName;
    }

    private string SaveResX(string source, string locale, XmlDocument doc, IProcessorFeedback feedback)
    {
      string resFilename = Path.GetFileNameWithoutExtension(source) + locale + ".resx";
      string fullName = Path.Combine(Path.GetDirectoryName(source),resFilename);

      doc.Save(fullName);
      feedback.AddResourceFile(fullName);

      return fullName;
    }

    private bool ParseHeader(string line, XmlDocument template)
    {
      if (line[0] == '#')
      {
        if (line.Length == 1)
          return true;

        if (line[1] == '!')
        {
          // Options
          int pos = line.IndexOf('=');
          if (pos > -1)
          {
            string opt = line.Substring(2, pos-2).Trim();
            string arg = line.Substring(pos+1).Trim();

            // Add to the headers
            XmlElement resHeaderEl = template.CreateElement("resheader");
            resHeaderEl.SetAttribute("name", opt);

            XmlElement resValueEl = template.CreateElement("value");
            resValueEl.AppendChild(template.CreateTextNode(arg));
            resHeaderEl.AppendChild(resValueEl);

            template.DocumentElement.AppendChild(resHeaderEl);
          }
        }
        return true;
      }

      return false;
    }


    public StringBuilder GenerateCode(string inputFileName, string FileNameSpace, IProcessorFeedback feedback, bool hasCustomToolNamespace)
    {
      ICodeGenerator generator = _provider.CreateGenerator();
      if (generator == null)
        throw new ApplicationException("No code generator available.");

      CodeCompileUnit ccu = Process(inputFileName, FileNameSpace, generator, hasCustomToolNamespace, feedback);

      // Dump code into the string builder
      StringWriter sw = new StringWriter();
      CodeGeneratorOptions opts = new CodeGeneratorOptions();
      opts.BracingStyle = "C";
      generator.GenerateCodeFromCompileUnit(ccu, sw, opts);                
      sw.Close();

      // Post process if necessary
      StringBuilder sbFile = sw.GetStringBuilder();
      PostProcessFile(ref sbFile, feedback);

      return sbFile;
    }

    private CodeCompileUnit Process(string inputFileName, string FileNameSpace, ICodeGenerator generator, bool hasCustomToolNamespace, IProcessorFeedback feedback)
    {
      SortedList sList = new SortedList();
      SRGenerationOptions options = new SRGenerationOptions();
      bool generateClass = true;

      //Iterate through every resource and build up a list of resources, and get options
      using(Stream inputStream = File.OpenRead(inputFileName))
      {
        XPathDocument doc = new XPathDocument(inputStream);

        // Get options
        XPathNodeIterator headerIt = doc.CreateNavigator().Select("//resheader");   
        while(headerIt.MoveNext())
        {
          string opt = headerIt.Current.GetAttribute("name", string.Empty);
          XPathNodeIterator valueIt = headerIt.Current.Select("value");
          if (valueIt.MoveNext())
          {
            string arg = valueIt.Current.Value;
                        
            switch (opt.ToLower())
            {
              case "accessor_class_accessibility":
                if (arg.ToLower() == "public")
                {
                  options.PublicSRClass = true;
                }
                break;

              case "culture_info":
                options.CultureInfoFragment = arg;
                break;

              case "generate_class":
                generateClass = bool.Parse(arg);
                break;

              case "resource_namespace":
                options.ResourceFileNamespace = arg;
                break;

              case "generator_usings":
                options.AdditionalUsing = arg.Split(',');
                break;
            }
          }
        }


        XPathNodeIterator it = doc.CreateNavigator().Select("//data");   
        while(it.MoveNext())
        {
          if (it.Current.GetAttribute("type", String.Empty).Length != 0)
          {
            continue; // Only interested in string resources
          }

          string key = it.Current.GetAttribute("name", String.Empty);
          string textValue = null;
          string commentValue = null;

          // Get the value and comment elements
          XPathNodeIterator valueIt = it.Current.SelectChildren(XPathNodeType.Element);
          while (valueIt.MoveNext())
          {
            if (valueIt.Current.Name == "value")
            {
              textValue = valueIt.Current.Value;
            }
            else if (valueIt.Current.Name == "comment")
            {
              commentValue = valueIt.Current.Value;
            }
          }

          sList.Add(key, new ResourceHolder(key, textValue, commentValue) );
        }
      }

      string className = Path.GetFileNameWithoutExtension(inputFileName);            
            
      CodeCompileUnit compileUnit = new CodeCompileUnit();

      //Just for VB.NET
      compileUnit.UserData.Add("AllowLateBound", false);
      compileUnit.UserData.Add("RequireVariableDeclaration", true);

      //Dummy namespace, so the Import statements would appear above the main namespace declaration
      CodeNamespace dummyNamespace = new CodeNamespace("");
      compileUnit.Namespaces.Add(dummyNamespace);


      if (hasCustomToolNamespace
        && options.ResourceFileNamespace == null)
      {
        string errorMessage = "You must specify the option resource_namespace when using a Custom Tool Namespace, to provide the namespace used for the resources.";
        //dummyNamespace.Comments.Add(new CodeCommentStatement(errorMessage));
        //feedback.LineError(errorMessage, null, 0);
        return new CodeSnippetCompileUnit("#error " + errorMessage);
      }

      if (!generateClass)
      {
        dummyNamespace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));
        dummyNamespace.Comments.Add(new CodeCommentStatement(" <autogeneratedinfo>"));
        dummyNamespace.Comments.Add(new CodeCommentStatement("     This code was generated by:"));
        dummyNamespace.Comments.Add(new CodeCommentStatement("       SR Resource Generator custom tool for VS.NET, by Martin Granell, Readify"));
        dummyNamespace.Comments.Add(new CodeCommentStatement(""));
        dummyNamespace.Comments.Add(new CodeCommentStatement("     NO CLASS WILL BE GENERATED "));
        dummyNamespace.Comments.Add(new CodeCommentStatement("     as the resource file contains the option: "));
        dummyNamespace.Comments.Add(new CodeCommentStatement("          generate_class = false. "));
        dummyNamespace.Comments.Add(new CodeCommentStatement(""));
        dummyNamespace.Comments.Add(new CodeCommentStatement("     The resource filename is:" + inputFileName));
        dummyNamespace.Comments.Add(new CodeCommentStatement(""));
        dummyNamespace.Comments.Add(new CodeCommentStatement("     Generated: " + DateTime.Now.ToString("f", new CultureInfo("en-US"))));
        dummyNamespace.Comments.Add(new CodeCommentStatement(" </autogeneratedinfo>"));
        dummyNamespace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));                                
        return compileUnit;
      }


      //Namespace Import
      foreach (string additionalUsing in options.AdditionalUsing)
      {
        dummyNamespace.Imports.Add(new CodeNamespaceImport(additionalUsing));
      }

      dummyNamespace.Imports.Add(new CodeNamespaceImport("System"));
      //dummyNamespace.Imports.Add(new CodeNamespaceImport("System.Resources"));
      //dummyNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
      //dummyNamespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics.CodeAnalysis"));     // added: vSv

      //Namespace
      CodeNamespace nSpace = new CodeNamespace(FileNameSpace);
      compileUnit.Namespaces.Add(nSpace);

      //Namespace comments
      nSpace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));
      nSpace.Comments.Add(new CodeCommentStatement(" <autogeneratedinfo>"));
      nSpace.Comments.Add(new CodeCommentStatement("     This code was generated by:"));
      nSpace.Comments.Add(new CodeCommentStatement("       SR Resource Generator custom tool for VS.NET, by Martin Granell, Readify"));
      nSpace.Comments.Add(new CodeCommentStatement(""));
      nSpace.Comments.Add(new CodeCommentStatement("     It contains classes defined from the contents of the resource file:"));
      nSpace.Comments.Add(new CodeCommentStatement("       " + inputFileName));
      nSpace.Comments.Add(new CodeCommentStatement(""));
      nSpace.Comments.Add(new CodeCommentStatement("     Generated: " + DateTime.Now.ToString("f", new CultureInfo("en-US"))));
      nSpace.Comments.Add(new CodeCommentStatement(" </autogeneratedinfo>"));
      nSpace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));

      // Define SR class
      CodeTypeDeclaration cSR = new CodeTypeDeclaration(className);

      // CodeDom Sucks. It doesn't allow support for specifying assembly visibility.
      // So we have to do a search and replace on the resulting text after we have generated
      // the file.
      cSR.TypeAttributes = TypeAttributes.Public;
      _convertPublicToInternal = !options.PublicSRClass;

      if (options.CultureInfoFragment == null)
      {
        // Create Static Culture property
        CodeMemberProperty cmStaticCulture = new CodeMemberProperty();
        cmStaticCulture.Name = "Culture";
        cmStaticCulture.Attributes = MemberAttributes.Public | MemberAttributes.Static;
        cmStaticCulture.Type = new CodeTypeReference(typeof(CultureInfo));
        cmStaticCulture.GetStatements.Add(
          new CodeMethodReturnStatement(
          new CodePropertyReferenceExpression(
          new CodeTypeReferenceExpression("Keys"), "Culture")));
        cmStaticCulture.SetStatements.Add(
          new CodeAssignStatement(
          new CodePropertyReferenceExpression(
          new CodeTypeReferenceExpression("Keys"), "Culture"), new CodeArgumentReferenceExpression("value")));
                
        cSR.Members.Add(cmStaticCulture);
      }

      // Define Keys class
      CodeTypeDeclaration cKeys = new CodeTypeDeclaration("Keys");

      cKeys.TypeAttributes = TypeAttributes.Public;

      // Create resource manager
      //			static ResourceManager resourceManager = 
      //            new ResourceManager("Microsoft.Practices.EnterpriseLibrary.Data.SR", typeof(Microsoft.Practices.EnterpriseLibrary.Data.SR).Module.Assembly );
      //
      CodeMemberField fResourceManager = new CodeMemberField(typeof(ResourceManager), "resourceManager");
      fResourceManager.Attributes = MemberAttributes.Static;   // NOTE: should be readonly!

      if (options.ResourceFileNamespace != null)
      {
        string resName = className;
        if (options.ResourceFileNamespace.Length > 0)
        {
          resName = options.ResourceFileNamespace.Length  + "." + resName;
        }
        fResourceManager.InitExpression = new CodeObjectCreateExpression(typeof(ResourceManager), new CodePrimitiveExpression(options.ResourceFileNamespace + "." + className), new CodePropertyReferenceExpression(new CodeTypeOfExpression(className), "Assembly"));
      }
      else
      {
        fResourceManager.InitExpression = new CodeObjectCreateExpression(typeof(ResourceManager), new CodeTypeOfExpression(className));
      }
      cKeys.Members.Add(fResourceManager);

      CodeExpression cultureCodeExp;
      if (options.CultureInfoFragment == null) // Add a property that is settable
      {
        // Culture field and property
        CodeMemberField fCulture = new CodeMemberField(typeof(CultureInfo), "_culture");
        fCulture.Attributes = MemberAttributes.Static;
        //fCulture.InitExpression = new CodePrimitiveExpression(null);    // Removed: VSV for FxCop rule CA1805
        cKeys.Members.Add(fCulture);

        CodeMemberProperty pCulture = new CodeMemberProperty();
        pCulture.Attributes = MemberAttributes.Static | MemberAttributes.Public;
        pCulture.Type = new CodeTypeReference( typeof(CultureInfo));
        pCulture.Name = "Culture";
        pCulture.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression(null, "_culture") ));
        pCulture.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression(null, "_culture"), new CodeArgumentReferenceExpression("value")));
        cKeys.Members.Add(pCulture);

        cultureCodeExp = new CodeFieldReferenceExpression(null, "_culture");
      }
      else
      {
        cultureCodeExp = new CodeSnippetExpression(options.CultureInfoFragment);
      }

      // added: vSv
      CodeAttributeDeclaration suppressMessageAttribute = new CodeAttributeDeclaration
        (
          new CodeTypeReference(typeof(SuppressMessageAttribute)),
          new CodeAttributeArgument(new CodePrimitiveExpression("Microsoft.Performance")),
          new CodeAttributeArgument(new CodePrimitiveExpression("CA1811:AvoidUncalledPrivateCode"))
        );

      // Get String methods

      // No parameters, just return the result of the resourceManager.GetString method.
      // 	return resourceManager.GetString( key, _culture );
      CodeMemberMethod mGetStringNoParams = new CodeMemberMethod();
      mGetStringNoParams.CustomAttributes.Add(suppressMessageAttribute);    // added: vSv
      mGetStringNoParams.Name = "GetString";
      mGetStringNoParams.Attributes = MemberAttributes.Public | MemberAttributes.Static;
      mGetStringNoParams.ReturnType = new CodeTypeReference(typeof(string));
      mGetStringNoParams.Parameters.Add( new CodeParameterDeclarationExpression(typeof(string), "key") );
      mGetStringNoParams.Statements.Add( new CodeMethodReturnStatement(
        new CodeMethodInvokeExpression(
        new CodeFieldReferenceExpression(null, "resourceManager"),
        "GetString",
        new CodeArgumentReferenceExpression("key"),
        cultureCodeExp)));
      cKeys.Members.Add(mGetStringNoParams);

      // With parameters, format the results using String.Format
      // return msg;
      CodeMemberMethod mGetStringWithParams = new CodeMemberMethod();
      mGetStringWithParams.CustomAttributes.Add(suppressMessageAttribute);    // added: vSv
      mGetStringWithParams.Name = "GetString";
      mGetStringWithParams.Attributes = MemberAttributes.Public | MemberAttributes.Static;
      mGetStringWithParams.ReturnType = new CodeTypeReference(typeof(string));
      mGetStringWithParams.Parameters.Add( 
        new CodeParameterDeclarationExpression(typeof(string), "key"));
      mGetStringWithParams.Parameters.Add( 
        new CodeParameterDeclarationExpression(typeof(object[]), "args"));

      // string msg = resourceManager.GetString( key, _culture );
      mGetStringWithParams.Statements.Add(
        new CodeVariableDeclarationStatement(typeof(string), "msg", 
        new CodeMethodInvokeExpression(
        new CodeFieldReferenceExpression(null, "resourceManager"),
        "GetString",
        new CodeArgumentReferenceExpression("key"),
        cultureCodeExp)));

      // Added: VSV for Resharper warning
      // if (String.IsNullOrEmpty(msg)) 
      // {
      //    msg = String.Format("Message not found for key: {0}", key);                                       
      // }
      mGetStringWithParams.Statements.Add
        (
          new CodeConditionStatement
          (
            new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(String)), "IsNullOrEmpty", new CodeVariableReferenceExpression("msg")),
            new CodeAssignStatement
            (
                new CodeVariableReferenceExpression("msg"), 
                new CodeMethodInvokeExpression
                (
                  new CodeTypeReferenceExpression(typeof(String)), 
                  "Format", 
                  cultureCodeExp, 
                  new CodePrimitiveExpression("Message not found for key: {0}"), 
                  new CodeArgumentReferenceExpression("key")
                )
            )
          )
        );

      // msg = string.Format( _culture, msg, args );                      
      mGetStringWithParams.Statements.Add(
        new CodeAssignStatement( new CodeVariableReferenceExpression("msg"),
        new CodeMethodInvokeExpression( new CodeTypeReferenceExpression(typeof(String)), "Format",
          cultureCodeExp,                                                                           // Added: VSV for FxCop rule CA1305
          new CodeVariableReferenceExpression("msg"), 
          new CodeArgumentReferenceExpression("args"))));

      // return msg
      mGetStringWithParams.Statements.Add( 
        new CodeMethodReturnStatement(new CodeVariableReferenceExpression("msg")));

      cKeys.Members.Add(mGetStringWithParams);

      IDictionaryEnumerator resEnumerator = sList.GetEnumerator();

      Regex safeRegex = new Regex("[^a-zA-Z0-9_]");

      // Create a class definition for each string entry  
      while(resEnumerator.MoveNext())
      {
        ResourceHolder resource = (ResourceHolder)resEnumerator.Value;

        // Create a safe identifier
        string safeKey = XmlConvert.EncodeName(resource.Key);

        // Deal with an initial numeral.
        if (safeKey[0] >= '0' && safeKey[0] <= '9')
          safeKey = "_" + safeKey;
                
        // Make sure we don't conflict with a language identifier.
        safeKey = generator.CreateValidIdentifier(safeKey);

        // The VS Generator always lower cases the first letter, which is not
        // wanted in this case.
        safeKey = char.ToUpper(safeKey[0], CultureInfo.InvariantCulture) + safeKey.Substring(1);

        /*
                //It is necessary to know how many replaceable parameters the string has
                string ParameterMatchExpression = @"(\{[^\}\{]+\})";
                MatchCollection mc = Regex.Matches(resource.Text, ParameterMatchExpression);
                //Don't include duplicates in count as String.Format argument can be specified
                //more than once, ie: "First: {0}, Second: {1}, First again: {0}" 
                StringCollection parameters = new StringCollection();
                foreach(Match match in mc)
                {
                    if(!parameters.Contains(match.Value))
                    {
                        parameters.Add(match.Value);
                    }
                }
                */


        // Get parameter names from comma sep names in comment
        string[] parameterNames = new string[0];
        if (resource.Comment != null)
        {
          parameterNames = resource.Comment.Split(',');
        }

                    
        if (parameterNames.Length > 0)
        {
          // Create as a method that takes the right number of parameters.

                        
          CodeMemberMethod mGetString = new CodeMemberMethod();
          mGetString.Name = safeKey;
          mGetString.Attributes = MemberAttributes.Static | MemberAttributes.Public;
          mGetString.ReturnType= new CodeTypeReference(typeof(string));

          //Create the argument lists
          CodeExpression[] args = new CodeExpression[parameterNames.Length];

          for(int i = 0; i < parameterNames.Length; i++)
          {
            string parameterName = parameterNames[i];
            parameterName = parameterName.Trim();
            if (parameterName.IndexOf(' ') > -1)
            {
              // parameter name includes type.
              string typeName = MapType(parameterName.Substring(0,parameterName.IndexOf(' ')).Trim());
              parameterName = parameterName.Substring(parameterName.IndexOf(' ')+1).Trim();

              mGetString.Parameters.Add(new CodeParameterDeclarationExpression(typeName, parameterName));
            }
            else
            {
              mGetString.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), parameterName));
            }
            args[i] = new CodeArgumentReferenceExpression(parameterName);
          }
                        
          mGetString.Statements.Add(
            new CodeMethodReturnStatement(
            new CodeMethodInvokeExpression(
            new CodeTypeReferenceExpression("Keys"), 
            "GetString", 
            new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Keys"), safeKey),
            new CodeArrayCreateExpression(typeof(object), args))));

          cSR.Members.Add(mGetString);
        }
        else
        {
          // Create as a property
          CodeMemberProperty pGetString = new CodeMemberProperty();
          pGetString.Name = safeKey;
          pGetString.Attributes = MemberAttributes.Static | MemberAttributes.Public;
          pGetString.Type= new CodeTypeReference(typeof(string));

          pGetString.GetStatements.Add(
            new CodeMethodReturnStatement(
            new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Keys"), "GetString", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Keys"), safeKey))));

          cSR.Members.Add(pGetString);
        }

        // Add a const field to the Keys class that contains the actual reference
        CodeMemberField fKey = new CodeMemberField(typeof(string), safeKey);
        fKey.InitExpression = new CodePrimitiveExpression(resource.Key);
        fKey.Attributes = MemberAttributes.Const | MemberAttributes.Public;
        cKeys.Members.Add(fKey);


      }
      cSR.Members.Add(cKeys);
      nSpace.Types.Add(cSR);

      return compileUnit;
    }

    private static string MapType(string typeName)
    {
      switch (typeName)
      {
        case "long":
          return "System.Int64";
        case "int":
          return "System.Int32";
        case "short":
          return "System.Int16";
        case "byte":
          return "System.Byte";
        case "ulong":
          return "System.UInt64";
        case "uint":
          return "System.UInt32";
        case "ushort":
          return "System.UInt16";
        case "sbyte":
          return "System.SByte";
        case "string":
          return "System.String";
        case "decimal":
          return "System.Decimal";
        case "float":
          return "System.Single";
        case "double":
          return "System.Double";
        case "bool":
          return "System.Boolean";
      }

      return typeName;
    }

    class ResourceHolder
    {
      public string Text;
      public string Comment;
      public string Key;
      public ResourceHolder(string key, string text, string comment )
      {
        Text = text;
        Comment = comment;
        Key = key;
      }
    }

    public void PostProcessFile(ref StringBuilder sb, IProcessorFeedback feedback)
    {
      if (!_convertPublicToInternal)
        return;

      if (_provider.FileExtension == "cs")
      {
        sb.Replace("public", "internal");
        sb.Replace("internal class", "internal static class");                      // Added: VSV for FxCop rule CA1812
        sb.Replace("static System.Resources.ResourceManager resourceManager", "static readonly System.Resources.ResourceManager resourceManager");    // Added: VSV for Resharper warning
      }
      else if (_provider.FileExtension == "vb")
      {
        sb.Replace("Public", "Friend");
        // TODO: add VB equivalents for additions above
      }
      else
      {
        feedback.LineError("Can only create Assembly visible classes in VB.NET and C#, please use option accessor_class_accessibility = public", string.Empty, 0);
      }

    }
  }
}
